use std::alloc::{self, Layout};
use std::fs::File;
use std::io::{self, BufReader};
use flate2::read::GzDecoder;
use tar::Archive;
use std::ffi::{CStr, CString};
use std::ptr;

#[no_mangle]
pub extern "C" fn list_files_in_tgz(file_path: *const i8) -> *mut *mut i8 {
    if file_path.is_null() {
        return return_error("File path is null");
    }

    let c_str = unsafe { CStr::from_ptr(file_path) };
    let file_path_str = match c_str.to_str() {
        Ok(path) => path,
        Err(_) => return return_error("Failed to convert file path to UTF-8 string"),
    };

    match list_files_in_tgz_rust(file_path_str) {
        Ok(paths) => {
            let mut c_strings = Vec::new();
            for path in paths {
                let c_string = match CString::new(path) {
                    Ok(s) => s.into_raw(),
                    Err(_) => return return_error("Failed to convert Rust string to C string"),
                };
                c_strings.push(c_string);
            }

            c_strings.push(ptr::null_mut()); // Null-terminate the array
            let array_ptr = c_strings.as_mut_ptr();
            std::mem::forget(c_strings); // Prevent Rust from freeing memory
            array_ptr
        }
        Err(e) => return return_error(&format!("Error processing tar file: {}", e)),
    }
}

fn list_files_in_tgz_rust(file_path: &str) -> io::Result<Vec<String>> {
    let file = File::open(file_path)?;
    let reader = BufReader::new(file);
    let decompressed = GzDecoder::new(reader);
    let mut archive = Archive::new(decompressed);
    
    let mut paths = Vec::new();
    for entry in archive.entries()? {
        let entry = entry?;
        let path = entry.path()?;
        paths.push(path.display().to_string());
    }
    Ok(paths)
}

fn return_error(message: &str) -> *mut *mut i8 {
    let error_message = CString::new(format!("ERROR: {}", message)).unwrap();
    let mut c_array: Vec<*mut i8> = vec![error_message.into_raw(), ptr::null_mut()];
    let array_ptr = c_array.as_mut_ptr();
    std::mem::forget(c_array); // Prevent Rust from freeing memory
    array_ptr
}

#[no_mangle]
pub extern "C" fn free_string_array(ptr: *mut *mut i8) {
    if ptr.is_null() {
        return;
    }

    let mut index = 0;
    unsafe {
        while !(*ptr.add(index)).is_null() {
            let str_ptr = *ptr.add(index);
            if !str_ptr.is_null() {
                let _ = CString::from_raw(str_ptr);
            }
            index += 1;
        }
        alloc::dealloc(ptr as *mut u8, Layout::array::<*mut i8>(index).unwrap());
    }
}